package com.agile.leetcode.hash;

import java.util.LinkedList;
import java.util.SortedMap;
import java.util.TreeMap;

/**
 * 带虚拟节点的一致性hash算法
 *
 * @Author KD
 * @Date 2021/2/5 11:12
 */
@SuppressWarnings("all")
public class ConsistencyHashWithVirtualNode {
    /**
     * 初始化ip
     */
    private static String[] ip = {"192.168.89.4", "192.168.89.5", "192.168.89.8", "192.168.89.9"};
    private static SortedMap<Integer, String> sortedMap = new TreeMap<Integer, String>();
    /**
     * 记录节点
     */
    private static LinkedList<String> realNode = new LinkedList<>();
    /**
     * 每个server对应的虚拟节点的个数
     */
    private static final int VIRTUALNUM = 5;

    /**
     * 初始化服务器位置
     */
    static {
        // 将真实节点添加到链表中
        for (String s : ip) {
            realNode.add(s);
        }
        // 遍历链表
        realNode.forEach(x -> {
            for (int i = 0; i < VIRTUALNUM; i++) {
                // 定义虚拟节点名称
                String virtualNodeName = x + "&&VN" + String.valueOf(i);
                // 拿到hash
                int hash = getHash(virtualNodeName);
                System.out.println("虚拟几点被添加了，hash值是" + hash);
                // 放入集合
                sortedMap.put(hash, virtualNodeName);
            }
        });
    }

    public static String getServer(String str) {
        // 拿到哈希值
        int hash = getHash(str);
        // 拿到比hash值他大的map
        SortedMap<Integer, String> tailMap = sortedMap.tailMap(hash);
        // 接收服务器
        String server;
        // 如果为空
        if (tailMap.isEmpty()) {
            // 拿到第一个
            server = sortedMap.get(sortedMap.firstKey());
        } else {
            // 拿到tailMap的第一个
            server = tailMap.get(tailMap.firstKey());
        }
        // 截取
        if (server != null) {
            server = server.substring(0, server.indexOf("&&"));
            return server;
        } else {
            return null;
        }
    }

    /**
     * hash算法
     *
     * @param ip
     * @return
     */
    public static int getHash(String ip) {
        final int p = 16777619;
        int hash = (int) 2166136261L;
        for (int i = 0; i < ip.length(); i++)
            hash = (hash ^ ip.charAt(i)) * p;
        hash += hash << 13;
        hash ^= hash >> 7;
        hash += hash << 3;
        hash ^= hash >> 17;
        hash += hash << 5;

        // 如果算出来的值为负数则取其绝对值
        if (hash < 0)
            hash = Math.abs(hash);
        return hash;
    }

    public static void main(String[] args) {
        String[] str = {"詹姆斯", "杜兰特", "安东尼", "韦德", "保罗"};
        for (String s : str) {
            String server = getServer(s);
            System.out.println(s + "落在的服务器是" + server);
        }
    }
}
